﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Net;
using System.Windows.Forms;
using BrightIdeasSoftware;
using LoLHQ.Properties;
using Microsoft.Win32;

namespace LoLHQ
{
    public partial class MainForm : Form
    {
        public class CompareStatsStruct
        {
            public string Name { set; get; }
            public string Base1 { set; get; }
            public string Level1 { set; get; }
            public string Diff { set; get; }
            public string Base2 { set; get; }
            public string Level2 { set; get; }
            public string Level18 { set; get; }
        }

        public class OverviewStruct
        {
            public string Name;
        }

        /// <summary>
        /// List of summoners names used performing lookup
        /// </summary>
        private List<string> lookupNames = new List<string>();

        /// <summary>
        /// Lookup library
        /// </summary>
        private LOLKingLib lookupLib = new LOLKingLib();

        /// <summary>
        /// Library for a single searches
        /// </summary>
        private LOLKingLib lolKingLib = new LOLKingLib();

        /// <summary>
        /// Library for accessing riot SQLite data
        /// </summary>
        private RiotLib riotLib = new RiotLib();

        /// <summary>
        /// Data storing/reading
        /// </summary>
        private LOLDb lolDb = new LOLDb();

        /// <summary>
        /// Location of LoL assets folder
        /// </summary>
        private string LoLAssetsPath = null;


        #region Init

        /// <summary>
        /// Inits application, first function that is executed when program starts
        /// </summary>
        private void InitAll()
        {
            // setup libraries
            lolKingLib.DataReceived += singleSearch_DataReceived;
            lookupLib.DataReceived += lookup_DataReceived;

            // setup rankedStats
            rankedStats.ListViewItemSorter = new Sorter();

            // setup image getters for ObjectListView controls
            iconColumn.ImageGetter = delegate(object rowObject)
            {
                return ((ChampListStruct)rowObject).Name; 
            };
            countersImgColumn.ImageGetter = delegate(object rowObject)
            {
                ChampCountersListStruct s = (ChampCountersListStruct)rowObject;

                // if duo - don't show pictures
                if (s.Name.Contains("/"))
                    return String.Empty;
                return s.Name;
            };

            // colorize counters list
            countersList.RowFormatter = delegate(OLVListItem olvi)
            {
                olvi.BackColor = ColorFromHSV((((ChampCountersListStruct)olvi.RowObject).Score + 200f) / 200f * 70f + 30f, 0.9, 0.9); 
            };

            LoadData();

            if (Settings.Default.CheckForUpdates)
                CheckForUpdates(false);

            CheckServer();
        }

        /// <summary>
        /// Loads all the required data.
        /// Called when application starts or when settings were changed.
        /// </summary>
        public void LoadData()
        {
            // clear everything
            riotLib.Data.Clear();
            riotLib.Abilities.Clear();
            riotLib.Skins.Clear();

            // PATHS

            // if Riot path wasn't found yet, try locating it
            if (String.IsNullOrEmpty(Settings.Default.LoLPath))
            {
                string installPath = GetRiotPath();
                if (installPath != null)
                    Settings.Default.LoLPath = installPath;
            }

            // if installation location is specified - try to get assets folder
            if (!String.IsNullOrEmpty(Settings.Default.LoLPath))
                LoLAssetsPath = GetRiotAssetsPath(Settings.Default.LoLPath);

            // make sure we can access LOL data
            if (!File.Exists(LoLAssetsPath + @"data\gameStats\gameStats_en_US.sqlite"))
            {
                tabContainer.TabPages.Remove(tabCounterPicks);
                tabContainer.TabPages.Remove(tabCounterPickOverview);
                tabContainer.TabPages.Remove(tabChampionInformation);

                (new OptionsForm(this)).Show();
                MessageBox.Show("Please specify League Of Legends installation location.");
            }
            else
            {
                if (!tabContainer.TabPages.Contains(tabCounterPicks))
                    tabContainer.TabPages.Add(tabCounterPicks);
                if (!tabContainer.TabPages.Contains(tabCounterPickOverview))
                    tabContainer.TabPages.Add(tabCounterPickOverview);
                if (!tabContainer.TabPages.Contains(tabChampionInformation))
                    tabContainer.TabPages.Add(tabChampionInformation);
            }

            // make sure there's at least personal list
            if (!File.Exists(Application.StartupPath + @"\My List.lolhq"))
                File.Create(Application.StartupPath + @"\My List.lolhq");

            // todo: what if DB is not found
            InitRiotData();

            LoadDbList();

            // load champions list
            foreach (RiotDataStruct data in riotLib.Data)
                infoList.Items.Add(data.DisplayName);
        }


        /// <summary>
        /// Reads data from Riot SQLite database and setups all images/paths
        /// </summary>
        void InitRiotData()
        {
            riotLib.DbLocation = LoLAssetsPath + @"data\gameStats\gameStats_en_US.sqlite";
            riotLib.GetChampionsData();
            riotLib.GetSkinsData();
            riotLib.GetAbilitiesData();

            // sort, so our list would appear alphabetic
            riotLib.Data.Sort((x, y) => string.Compare(x.Name, y.Name));


            // load images and champion lists
            List<ChampListStruct> champs = new List<ChampListStruct>();
            ImageList imgs = new ImageList();
            imgs.ColorDepth = ColorDepth.Depth32Bit;
            imgs.ImageSize = new Size(32, 32);
            foreach (RiotDataStruct champion in riotLib.Data)
            {
                champs.Add(new ChampListStruct() { Name = champion.Name, DisplayName = champion.DisplayName, Tag = champion.Tags });
                imgs.Images.Add(champion.Name, Image.FromFile(LoLAssetsPath + @"images\champions\" + champion.IconPath));
            }

            // assign images
            champsList.SmallImageList = imgs;
            countersList.SmallImageList = imgs;
            overviewList.SmallImageList = imgs;

            // update champion lists
            champsList.SetObjects(champs);
            champsList.Sort(1);
        }


        /// <summary>
        /// Retrieves LoL installation path. Returns NULL if can't be located in the registry
        /// </summary>
        /// <returns>Path to LoL installation folder or NULL</returns>
        private string GetRiotPath()
        {   
            RegistryKey rk = Registry.LocalMachine, candidate;
            rk = rk.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall");

            foreach (string key in rk.GetSubKeyNames())
            {
                candidate = rk.OpenSubKey(key);

                // make sure the key belongs to LoL
                if (candidate.GetValue("DisplayName") != null && candidate.GetValue("DisplayName").ToString().CompareTo("League of Legends") == 0)
                {
                    // try to get installation location
                    if (candidate.GetValue("InstallLocation") != null)
                        return candidate.GetValue("InstallLocation").ToString();
                }
            }

            return null;
        }

        /// <summary>
        /// Returns Riot's assets folder location
        /// </summary>
        /// <param name="installationPath">Installation path of LoL</param>
        /// <returns>Riot's assets folder</returns>
        private string GetRiotAssetsPath(string installationPath)
        {
            if (!Directory.Exists(installationPath + @"\RADS\projects\lol_air_client\releases"))
                return String.Empty;

            string[] directories = Directory.GetDirectories(installationPath + @"\RADS\projects\lol_air_client\releases");

            // check for the newest release
            DateTime newest = new DateTime(0);
            string resultPath = "";
            foreach (string dir in directories)
            {
                if (Directory.GetCreationTime(dir) > newest)
                {
                    newest = Directory.GetCreationTime(dir);
                    resultPath = dir;
                }
            }

            return resultPath + @"\deploy\assets\";
        }

        /// <summary>
        /// Gets all available DB files in the current directory and populates the list
        /// </summary>
        private void LoadDbList()
        {   
            databaseList.Items.Clear();
            databaseList2.Items.Clear();

            // populate the list
            string[] files = Directory.GetFiles(Application.StartupPath, "*.lolhq");
            foreach (string file in files)
            {
                databaseList.Items.Add(Path.GetFileNameWithoutExtension(file));
                databaseList2.Items.Add(Path.GetFileNameWithoutExtension(file));
            }

            if (!String.IsNullOrEmpty(Settings.Default.SelectedDb))
            {
                databaseList.SelectedItem = Settings.Default.SelectedDb;
                databaseList2.SelectedItem = Settings.Default.SelectedDb;
                lolDb.Filename = Settings.Default.SelectedDb + ".lolhq";
            }
        }

        #endregion


        #region Common
        /// <summary>
        /// Converts HSV to Color.
        /// Used for making pretty colors in lists
        /// </summary>
        /// <param name="hue"></param>
        /// <param name="saturation"></param>
        /// <param name="value"></param>
        /// <returns>Color object</returns>
        public static Color ColorFromHSV(double hue, double saturation, double value)
        {
            int hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6;
            double f = hue / 60 - Math.Floor(hue / 60);

            value = value * 255;
            int v = Convert.ToInt32(value);
            int p = Convert.ToInt32(value * (1 - saturation));
            int q = Convert.ToInt32(value * (1 - f * saturation));
            int t = Convert.ToInt32(value * (1 - (1 - f) * saturation));

            if (hi == 0)
                return Color.FromArgb(255, v, t, p);
            else if (hi == 1)
                return Color.FromArgb(255, q, v, p);
            else if (hi == 2)
                return Color.FromArgb(255, p, v, t);
            else if (hi == 3)
                return Color.FromArgb(255, p, q, v);
            else if (hi == 4)
                return Color.FromArgb(255, t, p, v);
            else
                return Color.FromArgb(255, v, p, q);
        }


        /// <summary>
        /// Calculates RScore from given parameters for a single champion.
        /// Assumptions for a perfect score:
        /// 50+ games
        /// 60%+ win ratio
        /// 150+ CS
        /// </summary>
        /// <param name="totalGames">Wins + Losses</param>
        /// <param name="winsPercentage">Percentages of won games</param>
        /// <param name="killsPerGame">Average kills per game</param>
        /// <param name="deathsPerGame">Average deaths per game</param>
        /// <param name="assistsPerGame">Average assists per game</param>
        /// <param name="CSPerGame">Average creep score per game</param>
        /// <returns>RScore value [0-100]</returns>
        public int RScore(int totalGames, double winsPercentage, double killsPerGame, double deathsPerGame, double assistsPerGame, double CSPerGame)
        {
            double result = 0;

            // can't judge from a single game
            if (totalGames <= 1)
                return 1;

            if (totalGames >= 50f)
                result += 30f;
            else
                result += totalGames / 50f * 30f;

            if (winsPercentage >= 60f)
                result += 35f;
            else
                result += winsPercentage / 60f * 35f;

            double kdar = 1f;
            if (deathsPerGame != 0f)
                kdar = (killsPerGame + (assistsPerGame / 5)) / deathsPerGame;

            if (kdar >= 1f)
                result += 25f;
            else
                result += kdar * 25f;

            if (CSPerGame >= 150f)
                result += 10f;
            else
                result += CSPerGame / 150f * 10f;

            return (int)Math.Round(result);
        }


        private void CheckForUpdates(bool showAlert)
        {
            WebClient wc = new WebClient();
            wc.DownloadStringAsync(new Uri(Settings.Default.UpdateCheckURL));
            wc.DownloadStringCompleted += delegate(object sender, DownloadStringCompletedEventArgs e) {
                try
                {
                    string[] tmp = e.Result.Split('|');
                    if (Settings.Default.Release < Int32.Parse(tmp[0]))
                    {
                        if (MessageBox.Show("New version is available. Download now?", "Update is available", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
                            Process.Start(Settings.Default.Homepage);
                    }
                    else
                        if (showAlert)
                            MessageBox.Show("No updates available.");
                }
                catch
                {
                }
            };
        }

        void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            throw new NotImplementedException();
        }
        
        #endregion


        #region Champion Manipulation
        /// <summary>
        /// Gets most played champion (minus those on ignore list (already returned))
        /// </summary>
        /// <param name="data">Where to search</param>
        /// <param name="ignore">Ignore list based on indexes</param>
        /// <returns>Returns an index of data array</returns>
        private int GetMostPlayedChampionChampion(LOLKingData data, List<int> ignore)
        {
            int max = 0, itemId = -1;

            // get most played champions
            for (int i = 0; i < data.RankedStats.Count; i++)
            {
                if (ignore.Contains(i))
                    continue;

                // wins + losses = total
                if (data.RankedStats[i].Wins + data.RankedStats[i].Losses > max)
                {
                    max = data.RankedStats[i].Wins + data.RankedStats[i].Losses;
                    itemId = i;
                }
            }

            return itemId;
        }


        /// <summary>
        /// Generates a unique ID for subject in DB
        /// </summary>
        /// <param name="key"></param>
        /// <param name="user1"></param>
        /// <param name="user2"></param>
        /// <param name="enemy1"></param>
        /// <param name="enemy2"></param>
        /// <returns></returns>
        private bool GetRecordId(string key, string user1, string user2, string enemy1, string enemy2, out string result)
        {
            string part1 = "", part2 = "";
            bool reversed = false;

            // first part
            // generate key for duo
            if (key == "d")
            {
                if (user1.CompareTo(user2) < 0)
                    part1 = user1 + "|" + user2;
                else
                    part1 = user2 + "|" + user1;
            }
            else // generate key for solo/jungle
                part1 = user1;

            // second part
            // generate key for duo
            if (key == "d")
            {
                if (enemy1.CompareTo(enemy2) < 0)
                    part2 = enemy1 + "|" + enemy2;
                else
                    part2 = enemy2 + "|" + enemy1;
            }
            else // generate key for solo/jungle
                part2 = enemy1;


            if (part1.CompareTo(part2) < 0)
                result = part1 + "|" + part2;
            else
            {
                result = part2 + "|" + part1;
                reversed = true;
            }


            return reversed;
        }

        #endregion
    }
}
